package cn.gogpay.dcb.api.service;

import cn.gogpay.dcb.api.common.util.ConvertUtils;
import cn.gogpay.dcb.api.dao.CarRepository;
import cn.gogpay.dcb.api.dao.CountRecordRepository;
import cn.gogpay.dcb.api.dao.NotifyQrCodeRecordRepository;
import cn.gogpay.dcb.api.dao.UserBusCardRepository;
import cn.gogpay.dcb.api.entity.CountRecord;
import cn.gogpay.dcb.api.entity.NotifyQrCodeRecord;
import cn.gogpay.dcb.api.entity.UserBusCard;
import cn.gogpay.dcb.api.feign.ApiPlatformClient;
import cn.gogpay.dcb.api.feign.CertCenterClient;
import cn.gogpay.dcb.api.feign.IcbcWalletClient;
import cn.gogpay.dcb.api.feign.form.DrivingV2Form;
import cn.gogpay.dcb.api.web.form.CountForm;
import cn.gogpay.dcb.api.web.form.NotifyForm;
import cn.gogpay.dcb.common.dto.ResultCode;
import cn.gogpay.dcb.common.dto.ResultDTO;
import cn.gogpay.dcb.common.exception.ServiceException;
import cn.gogpay.dcb.common.util.DateUtils;
import cn.gogpay.dcb.common.util.lang.StringUtils;
import cn.gogpay.dcb.h5base.dto.UserDTO;
import cn.gogpay.dcb.h5base.util.encrypt.EncryptUtils;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * description
 *
 * @author huangting@gogpay.cn
 * @date 2020/1/7 18:43
 */
@Service
public class OutService {
    private static final Logger logger = LoggerFactory.getLogger(OutService.class);

    @Autowired
    private CarRepository carRepository;

    @Autowired
    private NotifyQrCodeRecordRepository notifyQrCodeRecordRepository;

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private CountRecordRepository countRecordRepository;

    @Autowired
    private UserBusCardRepository userBusCardRepository;

    @Autowired
    private CertCenterClient certCenterClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserApiService userApiService;

    @Autowired
    private IcbcWalletClient icbcWalletClient;

    @Autowired
    private UserBusCardService userBusCardService;

    @Autowired
    private ApiPlatformClient apiPlatformClient;

    @Value("${sign.key}")
    private String key;

    @Value("${normal.count}")
    private Integer normalCount;

    @Value("${car.master.count}")
    private Integer carMasterCount;

    @Value("${driving.use:true}")
    private Boolean drivingUse;

    @Value("${free.test:true}")
    private Boolean freeTest;

    @Value("${free.test.phone}")
    private String freeTestPhone;

    public Integer getNormalCount() {
        return normalCount;
    }

    public Integer getCarMasterCount() {
        return carMasterCount;
    }

    /**
     * 获得result 中的数据
     *
     * @param resultDTO
     * @return
     */
    private JSONObject getData(ResultDTO resultDTO) {
        Integer status = resultDTO.getStatus();
        if (ResultCode.SUCCESS_CODE != status) {
            throw new ServiceException(resultDTO.getMsg());
        }
        JSONObject getObj = JSONObject.parseObject(JSONObject.toJSONString(resultDTO));
        return getObj;
    }

    public UserBusCard queryUserInfo(String cardNo) {
        Optional<List<UserBusCard>> dateDesc = userBusCardRepository.findAllByCardNoOrderByCreatedDateDesc(cardNo);
        if (dateDesc.isPresent() && dateDesc.get().size() > 0) {
            return dateDesc.get().get(0);
        } else {
            throw new ServiceException("未查询到该用户,该用户为开卡");
        }
    }

    /**
     * 是否为车主
     *
     * @param name
     * @return
     */
    public Boolean isCarMaster(String name, String cardNo) {
        if (drivingUse) {

            Boolean masterRedis = this.isMasterRedis(name, cardNo);
            return masterRedis;
        }

        return false;
    }

    public Boolean getMasterInfoByInterface(String name, String cardNo) {
        try {
            DrivingV2Form form = new DrivingV2Form();
            form.setCardNo(cardNo);
            form.setName(name);
            ResultDTO gonganjsz = apiPlatformClient.gonganjsz(form);
            JSONObject jsonObject = this.getData(gonganjsz);
            Boolean data = jsonObject.getBoolean("data");
            return data;
        } catch (Exception e) {
            logger.error("校验驾驶证信息失败，e:{}", e);
            return null;
        }
    }

    /**
     * 已使用次数
     *
     * @param form
     * @return
     */
    public Integer useCount(CountForm form, String userId) {
        String sql = "select count(*) as counts from notify_qr_code_record where pull_date = ?1 and user_id = ?2 and card_no = ?3 and phone = ?4 and result = '00'";
        Query nativeQuery = entityManager.createNativeQuery(sql);
        nativeQuery.setParameter(1, DateUtils.getNow());
        nativeQuery.setParameter(2, userId);
        nativeQuery.setParameter(3, form.getCardNo());
        nativeQuery.setParameter(4, form.getPhone());
        List resultList = nativeQuery.getResultList();
        BigInteger count = (BigInteger) resultList.get(0);
        return count.intValue();
    }

    /**
     * 判断是否能够免费
     *
     * @param form
     * @return
     */
    public Boolean canFree(CountForm form, String userId) {
        Integer count = this.useCount(form, userId);
        UserDTO userInfo = userApiService.getUserInfo(userId);
        logger.info("用户中心花费时间，[{}], [{}]", form.getRequestNumber(), System.currentTimeMillis());
        Boolean carMaster = this.isCarMaster(userInfo.getAuthName(), userInfo.getAuthCardNo());
        logger.info("是否为车主花费时间，[{}], [{}]", form.getRequestNumber(), System.currentTimeMillis());
        if (carMaster) {
            if (count >= carMasterCount) {
                return false;
            } else {
                return true;
            }
        } else {
            if (count >= normalCount) {
                return false;
            } else {
                return true;
            }
        }
    }

    /**
     * 是否可以免费
     *
     * @param form
     * @return
     */
    public ResultDTO isHaveCount(CountForm form) {
        return new ResultDTO(200, "查询成功", false);
    }

    /**
     * 是否可以免费
     *
     * @param form
     * @return
     */
    public ResultDTO isHaveCount1(CountForm form) {
        if (freeTest) {
            return new ResultDTO(200, "查询成功", false);
        }
        String requestNumber = form.getRequestNumber();
        Boolean aBoolean = this.checkRequestNumberExist(requestNumber);
        if (aBoolean) {
            logger.error("流水号已使用, data：{}", form);
            return new ResultDTO(10001, "流水号已使用");
        }

        UserBusCard userBusCard = this.queryUserInfo(form.getCardNo());

        String useId = userBusCard.getUseId();
        // 验签
        Boolean signCheck = false;
        String[] param = new String[4];
        param[0] = form.getPhone();
        param[1] = form.getCardNo();
        param[2] = form.getRequestNumber();
        param[3] = key;
        String sign = this.generateSing(param);
        if (sign.equals(form.getSign())) {
            signCheck = true;
        }
        if (!signCheck) {
            logger.error("验签失败，签名不正确, data：{}", form);
            return new ResultDTO(10002, "验签失败");
        }

        CountRecord record = ConvertUtils.copyProperties(form.toString(), CountRecord.class);
        record.setCountDate(DateUtils.getNow());
        Boolean canFree = this.canFree(form, useId);

        // 增加是否开通钱包的校验
        Boolean open = false;
        try {
            ResultDTO openAccount = icbcWalletClient.isOpenAccount(useId);
            JSONObject jsonObject = this.getData(openAccount);
            JSONObject data = jsonObject.getJSONObject("data");
            open = data.getBoolean("open");
            logger.info("钱包是否开通{},{}", open, form.getRequestNumber());
        } catch (Exception e) {
            logger.error("钱包是否开通查询异常，{}", e);
        }

        if (!open) {
            canFree = false;
        }

        // 增加余额不足1元不可免费
        /*try {
            ResultDTO balance = userBusCardService.getBalance(form.getPhone());
            JSONObject jsonObject = this.getData(balance);
            JSONObject data = jsonObject.getJSONObject("data");
            String money = data.getString("balance");
            double v = Double.parseDouble(money);
            if (v < 0.0) {
                canFree = false;
                logger.info("余额不足{}", v);
            }
        } catch (Exception e) {
            logger.error("余额查询异常，{}", e);
        }*/

        if (freeTest) {
            if (!freeTestPhone.contains(form.getPhone())) {
                canFree = false;
                logger.info("不处于测试账号");
            }
        }

        record.setHaveFreeCount(canFree);

        countRecordRepository.save(record);

        return new ResultDTO(200, "查询成功", canFree);
    }

    /**
     * 回调通知
     *
     * @param form
     * @return
     */
    public ResultDTO pullNotify(NotifyForm form) {
        // redis 校验流水号是否使用过
        String requestNumber = form.getRequestNumber();
        Boolean aBoolean = this.checkRequestNumberExist(requestNumber);
        if (aBoolean) {
            logger.error("流水号已使用, data：{}", form);
            return new ResultDTO(10001, "流水号已使用");
        }

        UserBusCard userBusCard = this.queryUserInfo(form.getCardNo());

        String useId = userBusCard.getUseId();

        // 验签
        Boolean signCheck = this.checkSign(form);
        if (!signCheck) {
            logger.error("验签失败，签名不正确, data：{}", form);
            return new ResultDTO(10002, "验签失败");
        }

        NotifyQrCodeRecord notifyQrCodeRecord = ConvertUtils.copyProperties(form.toString(), NotifyQrCodeRecord.class);
        notifyQrCodeRecord.setPullTime(form.getPullTime().toString());
        LocalDateTime time = LocalDateTime.ofEpochSecond(form.getPullTime() / 1000, 0, ZoneOffset.ofHours(8));
        notifyQrCodeRecord.setPullDate(DateUtils.format(time, "yyyy-MM-dd"));
        notifyQrCodeRecord.setUserId(useId);
        notifyQrCodeRecordRepository.save(notifyQrCodeRecord);
        return new ResultDTO(200, "回调成功");
    }

    public Boolean checkSign(NotifyForm form) {
        String[] param = new String[6];
        param[0] = form.getPhone();
        param[1] = form.getCardNo();
        param[2] = form.getResult();
        param[3] = form.getPullTime().toString();
        param[4] = form.getRequestNumber();
        param[5] = key;
        String sign = this.generateSing(param);
        if (sign.equals(form.getSign())) {
            return true;
        }
        return false;
    }

    /**
     * 生成sign
     *
     * @param args
     * @return
     */
    private String generateSing(String... args) {
        StringBuilder stringBuilder = new StringBuilder();
        for (String arg : args) {
            stringBuilder.append(arg);
        }
        return EncryptUtils.md5(stringBuilder.toString());

    }

    /**
     * 检查该流水号是否存在过
     *
     * @param requestNumber
     */
    public Boolean checkRequestNumberExist(String requestNumber) {
        String result = redisTemplate.opsForValue().get("sweep-yards-ride:free:" + requestNumber);
        if (StringUtils.isEmpty(result)) {
            redisTemplate.opsForValue().set("sweep-yards-ride:free:" + requestNumber, requestNumber, 60, TimeUnit.DAYS);
            return false;
        } else {
            return true;
        }
    }

    public Boolean isMasterRedis(String name, String cardNo) {
        String result = redisTemplate.opsForValue().get("sweep-yards-ride:cardNo:" + cardNo);
        if (StringUtils.isEmpty(result)) {
            Boolean info = this.getMasterInfoByInterface(name, cardNo);
            if (info == null) {
                result = Boolean.valueOf(false).toString();
            } else {
                result = info.toString();
                redisTemplate.opsForValue().set("sweep-yards-ride:cardNo:" + cardNo, result, 15, TimeUnit.DAYS);
                logger.info("从接口获取数据，cardNo:{}，result:{}", cardNo, result);
            }

        }
        return Boolean.valueOf(result);
    }

    public static void main(String[] args) {
        String[] param = new String[6];
        param[0] = "15685275886"; // getPhone
        param[1] = "3105072000680007276"; // getCardNo
        param[2] = "00"; // getResult
        param[3] = "1578403290"; // getPullTime
        param[4] = "5b3a5fcb3b4f11eab42e286ed488ca20"; // getRequestNumber
        param[5] = "YXA6mnPMvCGklLY9dUKs5isEbNwPwqI"; //key
        StringBuilder stringBuilder = new StringBuilder();
        for (String arg : param) {
            stringBuilder.append(arg);
        }
        String sign = EncryptUtils.md5(stringBuilder.toString());
        System.out.println("notify sign:" + sign);
        param = new String[4];
        param[0] = "13333333333"; // getPhone
        param[1] = "3105072000680006203"; // getCardNo
        param[2] = "3912d897-3b52-11ea-b42e-286ed488ca20"; // getRequestNumber
        param[3] = "YXA6mnPMvCGklLY9dUKs5isEbNwPwqI"; // key
        stringBuilder = new StringBuilder();
        for (String arg : param) {
            stringBuilder.append(arg);
        }
        sign = EncryptUtils.md5(stringBuilder.toString());
        System.out.println("canFree sign:" + sign);

        LocalDateTime time = LocalDateTime.ofEpochSecond(1578902995610L / 1000, 0, ZoneOffset.ofHours(8));
        String format = DateUtils.format(time, "yyyy-MM-dd");
        System.out.println(format);
    }
}
